package edu.uw.cs.cse461.sp12.OS;

import org.json.JSONException;
import org.json.JSONObject;

import edu.uw.cs.cse461.sp12.OS.DDNSException.*;

public class DDNSRRecord {
	public static final int PORT_NOT_SET = -1;
	public static final String IP_NOT_SET = null;
	public static final DDNSFullName ALIAS_NOT_SET = null;
	
	public enum RecordType {
		A,
		NS,
		SOA,
		CNAME
	}
	
	private DDNSFullName name;
	private RecordType type;
	
	// Valid for A, NS, and SOA types
	private String ip;
	private int port;
	// Valid for CNAME types
	private DDNSFullName alias;
	
	public DDNSRRecord(String name, RecordType type, String ip, int port) throws DDNSRuntimeException {
		if (type == RecordType.CNAME) {
			throw new DDNSRuntimeException("Wrong constructor for type " + type);
		}
		
		// Create a record of type A, NS, or SOA
		this.name = new DDNSFullName(name);
		this.type = type;
		this.ip = ip;
		this.port = port;
		this.alias = ALIAS_NOT_SET;
	}
	
	public DDNSRRecord(String name, String alias) {
		// Create a CNAME type
		this.name = new DDNSFullName(name);
		this.type = RecordType.CNAME;
		this.ip = IP_NOT_SET;
		this.port = PORT_NOT_SET;
		this.alias = new DDNSFullName(alias);
	}
	
	public DDNSRRecord(JSONObject json) throws JSONException {
		this.name = new DDNSFullName(json.getString("name"));
		this.type = RecordType.valueOf(json.getString("type"));
		
		this.ip = IP_NOT_SET;
		this.port = PORT_NOT_SET;
		this.alias = null;
		if (this.type == RecordType.CNAME) {
			// Get alias
			this.alias = new DDNSFullName(json.getString("alias"));
		} else {
			// Get ip and port
			this.ip = json.getString("ip");
			this.port = json.getInt("port");
		}
	}
	
	public JSONObject toJSON() throws JSONException {
		// Encode node as a JSON object
		JSONObject result = new JSONObject();
		result.put("name", this.name.toString());
		result.put("type", this.type.toString());
		if (this.type == RecordType.CNAME) {
			// Use alias
			result.put("alias", this.alias.toString());
		} else {
			// Use ip and port
			result.put("ip", this.ip);
			result.put("port", this.port);
		}
		
		return result;
	}
	
	public DDNSFullName getFullName() {
		return this.name;
	}
	
	public RecordType getType() {
		return this.type;
	}
	
	public void setIP(String ip) throws DDNSRuntimeException {
		if (type == RecordType.CNAME) {
			// Error.  Should not call
			throw new DDNSRuntimeException("Record of type " + type + " does not have an IP.");
		} else {
			this.ip= ip;
		}
	}
	
	public String getIP() throws DDNSRuntimeException {
		if (type == RecordType.CNAME) {
			// Error.  Should not call
			throw new DDNSRuntimeException("Record of type " + type + " does not have an IP.");
		} else {
			return ip;
		}
	}
	
	public void setPort(int port) throws DDNSRuntimeException {
		if (type == RecordType.CNAME) {
			// Error.  Should not call
			throw new DDNSRuntimeException("Record of type " + type + " does not have a port.");
		} else {
			this.port= port;
		}
	}
	
	public int getPort() throws DDNSRuntimeException {
		if (type == RecordType.CNAME) {
			// Error.  Should not call
			throw new DDNSRuntimeException("Record of type " + type + " does not have a port.");
		} else {
			return port;
		}
	}
	
	public DDNSFullName getAlias() throws DDNSRuntimeException {
		if (type == RecordType.CNAME) {
			return alias;
		} else {
			// Error.  Should not call
			throw new DDNSRuntimeException("Record of type " + type + " does not have an alias.");
		}
	}

	public boolean isRegistered() {
		if (this.type == RecordType.CNAME) {
			return (this.alias != ALIAS_NOT_SET);
		} else {
			return (this.ip != IP_NOT_SET) &&
				   (this.port != PORT_NOT_SET);
			
		}
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		
		builder.append("Type: ");
		builder.append(this.type.toString());
		if (this.type == RecordType.CNAME) {
			builder.append(", Alias: ");
			builder.append(this.alias.toString());
		} else {
			if (this.ip == IP_NOT_SET) {
				// Not set
				builder.append(", No current address");
			} else {
				builder.append(", IP: ");
				builder.append(this.ip);
				builder.append(", Port: ");
				builder.append(this.port);
			}
		}
		return builder.toString();
	}
}
